package cn.blnp.net.flowable.listen.exec;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <h3>监听器-执行监听器</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/21 9:17
 */
@Slf4j
@SpringBootTest
public class ExecListenTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private ManagementService managementService;

    /**
     *  <h2>执行监听器配置</h2>
     *  <p>
     *      在流程实例执行过程中触发某个事件时，Flowable提供的执行监听器可以捕获该事件并执行相应的外部Java代码，或者对指定的表达式求值。执行监听器可以
     * 捕获的事件主要如下：
     * <<ol>
     *     <li>流程实例的开始和结束（在流程上配置）；</li>
     *     <li>顺序流的执行（在顺序流上配置）；</li>
     *     <li>任务节点的开始和结束（配置在任务节点上）；</li>
     *     <li>网关的开始和结束（配置在网关上）；</li>
     *     <li>中间事件的开始和结束（配置在中间捕获事件和中间抛出事件上）；</li>
     *     <li>开始事件或结束事件的开始和结束（配置在开始事件和结束事件上）。</li>
     * </ol>
     *  </p>
     *  <h3>配置方式</h3>
     *  <p>
     *      执行监听器能添加到流程、各类节点和顺序流中，定义在其extensionElements的子元素中，并使用Flowable命名空间。示例代码如下：
     *   <pre>
     *       {@code
     *          <process id="executionListenersProcess">
     *              <extensionElements>
     *                  <!-- 配置流程的执行监听器 -->
     *                  <flowable:executionListener event="start" class="com.bpm.example.executionlistener.demo.listener.ProcessExecutionListener"/>
     *              </extensionElements>
     *              <startEvent id="startEvent" />
     *              <sequenceFlow id="executionListenerSequenceFlow" sourceRef="startEvent" targetRef="userTask">
     *                  <extensionElements>
     *                      <!-- 配置连线的执行监听器 -->
     *                      <flowable:executionListener event="take" class="com.bpm.example.executionlistener.demo.listener.SequenceFlowExecutionListener"/>
     *                  </extensionElements>
     *              </sequenceFlow>
     *              <userTask id="executionListenerUserTask" >
     *                  <extensionElements>
     *                      <!-- 配置用户任务节点的执行监听器 -->
     *                      <flowable:executionListener expression="${myBean.testMethod(execution)}" event="end" />
     *                  </extensionElements>
     *              </userTask>
     *          </process>
     *       }
     *   </pre>
     *       在以上流程定义中，码通过flowable:executionListener元素分别为流程executionListenersProcess、顺序流executionListenerSequenceFlow和用户任务executionListenerUserTask配置
     *   了执行监听器。通过flowable:executionListener配置执行监听器需要指定事件类型和监听器的实现方式。flowable:executionListener提供了event属性用于设置事件类型，该属性必须配置，可设置的属
     *   性值有start、end和take。
     *  </p>
     *  <h4>1、通过 class 属性配置执行监听器</h4>
     *  <pre>
     *      {@code
     *          <userTask id="myTask" name="用户任务1" >
     *              <extensionElements>
     *                  <flowable:executionListener class="cn.blnp.net.flowable.listen.exec.MyUserTaskExecutionListener" event="end">
     *                      <flowable:field name="fixedValue" stringValue="这是一个固定值." />
     *                      <flowable:field name="dynamicValue" expression="${myVar}" />
     *                  </flowable:executionListener>
     *              </extensionElements>
     *          </userTask>
     *      }
     *  </pre>
     *  > 监听实现详见：{@link MyUserTaskExecutionListener}
     *
     *  <h4>2、通过expression属性配置执行监听器</h4>
     *  <pre>
     *      {@code
     *          <process id="executionListenersProcess">
     *              <extensionElements>
     *                  <flowable:executionListener event="end" expression="${myProcessExecutionListenerBean.testMethod(execution)}"/>
     *              </extensionElements>
     *          </process>
     *      }
     *  </pre>
     *  > 实现对象详见：{@link MyProcessExecutionListenerBean}
     *
     *  <h4>3、通过delegateExpression属性配置执行监听器</h4>
     *  <pre>
     *      {@code
     *          <sequenceFlow sourceRef="startEvent" targetRef="userTask">
     *              <extensionElements>
     *                  <flowable:executionListener delegateExpression="${myExecutionListenerBean}" event="take" />
     *              </extensionElements>
     *          </sequenceFlow>
     *      }
     *  </pre>
     *  > 实现对象详见：{@link MyExecutionListenerBean}
     *
     *  <h4>4、通过配置脚本配置执行监听器</h4>
     *  <pre>
     *      {@code
     *          <startEvent id="startEvent" name="开始节点">
     *              <extensionElements>
     *                  <flowable:executionListener event="end" class="org.flowable.engine.impl.bpmn.listener.ScriptExecutionListener">
     *                      <flowable:field name="script">
     *                          <flowable:string>
     *                              println("============通过脚本指定的监听器开始============");
     *                              def activityId = execution.getCurrentActivityId();
     *                              def eventName = execution.getEventName();
     *                              println("事件名称:" + ${eventName});
     *                              println("activityId:" + ${activityId});
     *                              println("============通过脚本指定的监听器结束============");
     *                          </flowable:string>
     *                     </flowable:field>
     *                  <flowable:field name="language" stringValue="groovy"/>
     *                  <flowable:field name="resultVariable" stringValue="myVar"/>
     *                </flowable:executionListener>
     *            </extensionElements>
     *         </startEvent>
     *      }
     *  </pre>
     *
     *  <h2>案例演示</h2>
     *  <p>
     *      流程包含开始事件、用户任务、结束事件和顺序流，通过配置各种事件监听器，可以在流程执行过程中观察各类事件的触发顺序。<br/>.
     *      <b>流程定义文件：</b>{@link flow/executionListenerProcess.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/21/682d4b0557387.png" />
     *  </p>
     *  流程从发起到结束的整个流转过程中，事件触发的顺序为流程start事件→开始事件start事件→开始事件end事件→顺序流start事件→顺
     * 序流take事件→顺序流end事件→用户任务start事件→用户任务end事件→顺序流start事件→顺序流take事件→顺序流end事件→结束事件start事件→结束事件
     * end事件→流程end事件，如图16.2所示。另外，流程结束后查询的流程变量中，nodeEvent1、nodeEvent2、nodeEvent3和nodeEvent4这4个变量是由配置脚
     * 本的resultVariable属性指定的。<br/>
     * <img src="https://cdn4.winhlb.com/2025/05/21/682d4f46825be.png" />
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("流程执行监听器-演示案例")
                .addClasspathResource("flow/executionListenerProcess.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}，定义名称：{}", procDef.getId(), procDef.getName());
        //设置流程变量
        Map<String, Object> varMap1 = MapUtil.builder(new HashMap<String, Object>())
                .put("nowTime1", new Date())
                //.put("myProcessExecutionListenerBean", new MyProcessExecutionListenerBean())
                //.put("mySequenceFlowExecutionListenerBean", new MySequenceFlowExecutionListener())
                //.put("myUserTaskExecutionListenerBean", new MyUserTaskExecutionListener())
                .build();
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId(), varMap1);
        //查询任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(procInst.getId()).singleResult();
        Map<String, Object> varMap2 = MapUtil.builder(new HashMap<String, Object>())
                .put("nowTime2", new Date())
                .build();
        //办理任务
        taskService.complete(task.getId(), varMap2);
        //查询并打印流程变量
        List<HistoricVariableInstance> hisVars = historyService
                .createHistoricVariableInstanceQuery()
                .processInstanceId(procInst.getId()).list();
        hisVars.stream().forEach(hisVar -> log.info("流程变量名：{}，变量值：{}",
                hisVar.getVariableName(), hisVar.getValue()));

        /**
         *  演示结果：
         *  2025-05-21 11:55:17.194  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 部署流程定义：67f4678a-35f7-11f0-a114-50ebf6ec8f6d
         * 2025-05-21 11:55:17.199  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程定义编号：executionListenerProcess:7:684e6e6d-35f7-11f0-a114-50ebf6ec8f6d，定义名称：执行监听器使用示例流程
         * 2025-05-21 11:55:17.236  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : ============通过expression指定的监听器开始============
         * 2025-05-21 11:55:17.236  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : 事件名称:start
         * 2025-05-21 11:55:17.236  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : activityId:null
         * 2025-05-21 11:55:17.236  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : ============通过expression指定的监听器结束============
         * 通过脚本指定的监听器：Id为startEvent的开始事件的start事件触发
         * 通过脚本指定的监听器：Id为startEvent的开始事件的end事件触发
         * 2025-05-21 11:55:17.568  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow1的顺序流的start事件触发
         * 2025-05-21 11:55:17.568  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow1的顺序流的take事件触发
         * 2025-05-21 11:55:17.568  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow1的顺序流的end事件触发
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : ============通过class指定的监听器开始============
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : 事件名称:start
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : activityId:userTask
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : fixedValue属性值为:这是userTask的start事件注入的固定值
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : dynamicValue属性值为:Wed May 21 11:55:17 CST 2025
         * 2025-05-21 11:55:17.569  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : ============通过class指定的监听器结束============
         * 2025-05-21 11:55:17.651  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : ============通过class指定的监听器开始============
         * 2025-05-21 11:55:17.651  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : 事件名称:end
         * 2025-05-21 11:55:17.651  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : activityId:userTask
         * 2025-05-21 11:55:17.651  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : fixedValue属性值为:这是userTask的end事件注入的固定值
         * 2025-05-21 11:55:17.652  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : dynamicValue属性值为:Wed May 21 11:55:17 CST 2025
         * 2025-05-21 11:55:17.652  INFO 46788 --- [           main] c.b.n.f.l.e.MyUserTaskExecutionListener  : ============通过class指定的监听器结束============
         * 2025-05-21 11:55:17.658  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow2的顺序流的start事件触发
         * 2025-05-21 11:55:17.659  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow2的顺序流的take事件触发
         * 2025-05-21 11:55:17.659  INFO 46788 --- [           main] .n.f.l.e.MySequenceFlowExecutionListener : 通过delegateExpression指定的监听器：Id为seqFlow2的顺序流的end事件触发
         * 通过脚本指定的监听器：Id为endEvent的结束事件的start事件触发
         * 通过脚本指定的监听器：Id为endEvent的结束事件的end事件触发
         * 2025-05-21 11:55:17.737  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : ============通过expression指定的监听器开始============
         * 2025-05-21 11:55:17.737  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : 事件名称:end
         * 2025-05-21 11:55:17.737  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : activityId:null
         * 2025-05-21 11:55:17.737  INFO 46788 --- [           main] b.n.f.l.e.MyProcessExecutionListenerBean : ============通过expression指定的监听器结束============
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nowTime1，变量值：Wed May 21 11:55:17 CST 2025
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nodeEvent1，变量值：startEvent_start
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nodeEvent2，变量值：startEvent_end
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nowTime2，变量值：Wed May 21 11:55:17 CST 2025
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nodeEvent3，变量值：endEvent_start
         * 2025-05-21 11:55:17.804  INFO 46788 --- [           main] c.b.n.f.listen.exec.ExecListenTest       : 流程变量名：nodeEvent4，变量值：endEvent_end
         **/
    }
}
